<template>
	<view class="file-uploader">
		<!-- 已上传/已选择文件列表 -->
		<view class="file-list" v-if="fileList.length > 0">
			<view class="file-item" v-for="(file, index) in fileList" :key="index"
				:style="{ width: itemWidth, height: itemHeight }">
				<!-- 图片预览 -->
				<image v-if="props.showType == 'image'" :src="file.url || file.path" class="file-preview"
					mode="aspectFill" @click="previewImage(index)"></image>
				<!-- 视频预览 -->
				<video v-else-if="props.showType == 'video'" :src="file.url || file.path" class="file-preview"
					:controls="false" @click="previewVideo(file.url || file.path)"></video>
				<!-- 其他文件类型（可自定义图标或样式） -->
				<view v-else class="file-other">
					<text class="file-name">{{ getFileName(file.name || file.path) }}</text>
				</view>

				<!-- 删除按钮 -->
				<view class="delete-btn" @click="removeFile(index)">
					<text class="delete-icon">×</text>
				</view>

				<!-- 上传进度/状态 -->
				<view v-if="file.uploading" class="upload-status uploading">
					<text>{{ file.progress }}%</text>
				</view>
				<view v-else-if="file.uploadError" class="upload-status error">
					<text>上传失败</text>
				</view>
				<view v-else-if="file.status=='success'" class="upload-status success">
					<text>✓</text>
				</view>
			</view>
		</view>

		<!-- 上传按钮 -->
		<view v-if="fileList.length < maxCount" class="upload-btn" :style="{ width: itemWidth, height: itemHeight }"
			@click="selectFile">
			<view class="upload-icon">+</view>
			<text class="upload-text">上传</text>
		</view>
	</view>
</template>

<script setup>
	import {
		ref,
		watch,
		onMounted,
		computed
	} from 'vue'

	// ========== Props 定义 ==========
	const props = defineProps({
		// 最大上传数量
		maxCount: {
			type: Number,
			default: 9
		},
		// 接受的文件类型 ('image', 'video', 'file')
		accept: {
			type: String,
			default: 'image'
		},
		// 每个文件项的宽度
		itemWidth: {
			type: String,
			default: '160rpx'
		},
		// 每个文件项的高度
		itemHeight: {
			type: String,
			default: '160rpx'
		},
		// 初始文件列表
		modelValue: { // Vue 3 中 v-model 默认绑定 modelValue
			type: Array,
			default: () => []
		},
		// 以什么图片还是列表形式展示列表('image', 'video', 'list')
		showType: {
			type: String,
			default: 'image'
		},
	})

	// ========== Emits 定义 ==========
	const emit = defineEmits(['update:modelValue', 'upload', 'error', 'change'])

	// ========== 响应式数据 ==========
	// 使用 ref 存储文件列表
	const fileList = computed({
		get: () => {
			if (!props.modelValue) return []
			// if (Array.isArray(props.modelValue) && props.modelValue.length > 0) {
			// 	if (JSON.stringify(props.modelValue) !== JSON.stringify(fileList.value)) {
			// 		fileList.value = props.modelValue.map(file => ({
			// 			...file,
			// 			uploading: file.uploading ?? false,
			// 			uploadError: file.uploadError ?? false,
			// 			progress: file.progress ?? 0
			// 		}))
			// 	}
			// }
			return props.modelValue
		},
		set: (val) => {
			let fileArr = val.map(({
				path,
				url,
				name,
				size,
				type,
				success,
				res,
				uploadError,
				status,
			}) => ({
				path,
				url,
				name,
				size,
				type,
				success,
				res,
				uploadError,
				status,
			}))
			console.log('fileArr===>', fileArr);
			emit('change', fileArr)
			emit('update:modelValue', fileArr)
		}
	})

	// ========== 生命周期 ==========
	onMounted(() => {
		// 组件挂载时，用 props 初始化 fileList
		initFileList()
	})


	// ========== Watchers ==========
	// 监听 modelValue 变化 (父组件通过 v-model 传入)
	// watch(
	// 	() => props.modelValue,
	// 	(newVal) => {
	// 		if (JSON.stringify(newVal) !== JSON.stringify(fileList.value)) {
	// 			fileList.value = newVal.map(file => ({
	// 				...file,
	// 				uploading: file.uploading ?? false,
	// 				uploadError: file.uploadError ?? false,
	// 				progress: file.progress ?? 0
	// 			}))
	// 		}
	// 	}, {
	// 		immediate: true,
	// 		deep: true
	// 	}
	// )

	// 监听 fileList 变化，同步给父组件
	// watch(
	// 	fileList,
	// 	(newList) => {
	// 		// 过滤掉仅包含上传状态的属性，只保留核心信息
	// 		const uploadList = newList.map(({
	// 			path,
	// 			url,
	// 			name,
	// 			size,
	// 			type,
	// 			res,
	// 		}) => ({
	// 			path,
	// 			url,
	// 			name,
	// 			size,
	// 			type,
	// 			res,
	// 		}))
	// 		emit('update:modelValue', uploadList) // 触发 v-model 更新
	// 	}, {
	// 		deep: true
	// 	}
	// )

	// ========== 方法 ==========
	/**
	 * 初始化 fileList
	 */
	function initFileList() {
		fileList.value = props.modelValue.map(file => ({
			...file,
			uploading: file.uploading ?? false,
			uploadError: file.uploadError ?? false,
			progress: file.progress ?? 0
		}))
	}

	/**
	 * 选择文件
	 */
	function selectFile() {
		// 计算还能选择的数量
		const remainingCount = props.maxCount - fileList.value.length
		if (remainingCount <= 0) return

		// 根据 accept 类型调用不同的选择方法
		if (props.accept === 'image') {
			chooseImage(remainingCount)
		} else if (props.accept === 'video') {
			chooseVideo()
		} else {
			chooseFile(remainingCount)
		}
	}

	function fileItem(file) {
		let obj = {
			...file,
			path: file.path,
			url: file.path,
			tempFilePath: file.path,
			name: file.name || getFileName(file.path),
			size: file.size,
			type: file.type || 'image/jpeg',
			uploading: false,
			uploadError: false,
			progress: 0,
			onSuccess: function(res, field = 'res') {
				fileList.value = fileList.value.map(item => {
					if (item.name == this.name) {
						item.status = "success"
						item[field] = res
					}
					return item
				})
			},
			onError: function() {
				fileList.value = fileList.value.map(item => {
					if (item.name == this.name) {
						item.status = "error"
						item.uploadError = true
					}
					console.log('错误====>', item);
					return item
				})
			},
		}
		return obj
	}

	const tempFiles = ref([])
	/**
	 * 选择图片
	 */
	function chooseImage(count) {
		uni.chooseImage({
			count: count,
			sourceType: ['album', 'camera'],
			success: (res) => {
				tempFiles.value = res.tempFiles.map(file => fileItem(file))
				emit('upload', tempFiles.value) // 通用上传事件
				fileList.value = [...fileList.value, ...tempFiles.value]
			},
			fail: (err) => {
				console.error('选择图片失败:', err)
				emit('error', {
					type: 'select',
					message: '选择图片失败',
					error: err
				})
			}
		})
	}

	/**
	 * 选择视频
	 */
	function chooseVideo() {
		uni.chooseVideo({
			sourceType: ['album', 'camera'],
			maxDuration: 60,
			camera: 'back',
			success: (res) => {
				tempFiles.value = [fileItem(res)]
				emit('upload', tempFiles.value) // 通用上传事件
				fileList.value = [...fileList.value, ...tempFiles.value]
			},
			fail: (err) => {
				console.error('选择视频失败:', err)
				emit('error', {
					type: 'select',
					message: '选择视频失败',
					error: err
				})
			}
		})
	}

	/**
	 * 选择其他文件
	 */
	function chooseFile(count) {
		uni.chooseMessageFile({
			count: count,
			type: props.accept === 'file' ? 'file' : 'all',
			extension: '',
			success: (res) => {
				tempFiles.value = res.tempFiles.map(file => fileItem(file))
				emit('upload', tempFiles.value) // 通用上传事件
				fileList.value = [...fileList.value, ...tempFiles.value]
			},
			fail: (err) => {
				console.error('选择文件失败:', err)
				emit('error', {
					type: 'select',
					message: '选择文件失败',
					error: err
				})
			}
		})
	}

	/**
	 * 删除文件
	 */
	function removeFile(index) {
		// 使用 splice 会触发响应式更新
		fileList.value.splice(index, 1)
	}

	/**
	 * 预览图片
	 */
	function previewImage(index) {
		const urls = fileList.value
			.filter(file => file.type && file.type.startsWith('image/'))
			.map(file => file.url || file.path)
		uni.previewImage({
			current: urls[index],
			urls: urls
		})
	}

	/**
	 * 预览视频
	 */
	function previewVideo(videoUrl) {
		uni.showModal({
			title: '视频预览',
			content: '是否在浏览器中打开视频？',
			success: function(res) {
				if (res.confirm) {
					// #ifdef H5
					window.open(videoUrl, '_blank')
					// #endif
					// #ifndef H5
					console.log('视频链接:', videoUrl)
					// emit 事件让父组件处理
					// #endif
				}
			}
		})
	}

	/**
	 * 手动上传单个文件
	 */
	async function uploadFile(file, index, uploadUrl) {
		if (file.url && !file.uploading && !file.uploadError) {
			return Promise.resolve({
				index,
				file,
				success: true,
				response: null
			})
		}

		// 直接修改响应式对象的属性
		file.uploading = true
		file.uploadError = false
		file.progress = 0

		return new Promise((resolve, reject) => {
			const uploadTask = uni.uploadFile({
				url: uploadUrl,
				filePath: file.path,
				name: 'file',
				success: (uploadRes) => {
					try {
						const data = JSON.parse(uploadRes.data)
						if (data.code === 0 || data.success) {
							const fileUrl = data.data?.url || data.url
							file.url = fileUrl
							file.uploading = false
							file.progress = 100
							resolve({
								index,
								file,
								success: true,
								response: data
							})
						} else {
							throw new Error(data.message || '上传失败')
						}
					} catch (error) {
						file.uploading = false
						file.uploadError = true
						reject({
							index,
							file,
							success: false,
							error: error.message
						})
					}
				},
				fail: (err) => {
					file.uploading = false
					file.uploadError = true
					reject({
						index,
						file,
						success: false,
						error: err.errMsg || '上传请求失败'
					})
				}
			})

			uploadTask.onProgressUpdate((res) => {
				file.progress = res.progress
			})
		})
	}

	/**
	 * 手动上传所有待上传文件
	 */
	async function uploadAll(uploadUrl) {
		const uploadPromises = fileList.value.map((file, index) =>
			uploadFile(file, index, uploadUrl).catch(err => err)
		)
		return Promise.all(uploadPromises)
	}

	/**
	 * 获取文件名
	 */
	function getFileName(path) {
		if (!path) return '未知文件'
		return path.split('/').pop().split('\\').pop() || '未知文件'
	}

	/**
	 * 根据文件名猜测 MIME Type
	 */
	function getMimeType(filename) {
		const ext = filename.split('.').pop()?.toLowerCase()
		const mimeMap = {
			'jpg': 'image/jpeg',
			'jpeg': 'image/jpeg',
			'png': 'image/png',
			'gif': 'image/gif',
			'bmp': 'image/bmp',
			'mp4': 'video/mp4',
			'avi': 'video/avi',
			'mov': 'video/quicktime',
			'wmv': 'video/x-ms-wmv',
			'pdf': 'application/pdf',
			'doc': 'application/msword',
			'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
			'xls': 'application/vnd.ms-excel',
			'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
			'ppt': 'application/vnd.ms-powerpoint',
			'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
		}
		return mimeMap[ext] || 'application/octet-stream'
	}

	// ========== 暴露给父组件的方法 ==========
	// 使用 defineExpose 暴露方法，父组件可以通过 ref 调用
	defineExpose({
		uploadFile,
		uploadAll,
		fileList // 也可以暴露 fileList，但通常通过 v-model 或事件获取
	})
</script>

<style scoped>
	.file-uploader {
		display: flex;
		flex-wrap: wrap;
		gap: 20rpx;
	}

	.file-list,
	.upload-btn {
		position: relative;
		border: 2rpx dashed #d9d9d9;
		border-radius: 12rpx;
		overflow: hidden;
		box-sizing: border-box;
	}

	.file-item {
		position: relative;
		background-color: #f9f9f9;
	}

	.file-preview {
		width: 100%;
		height: 100%;
	}

	.file-other {
		width: 100%;
		height: 100%;
		display: flex;
		align-items: center;
		justify-content: center;
		background-color: #f0f0f0;
		padding: 10rpx;
		box-sizing: border-box;
	}

	.file-name {
		font-size: 24rpx;
		color: #666;
		text-align: center;
		word-break: break-all;
		line-height: 1.4;
	}

	.upload-btn {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		background-color: #fafafa;
	}

	.upload-icon {
		font-size: 48rpx;
		color: #999;
		line-height: 1;
	}

	.upload-text {
		margin-top: 10rpx;
		font-size: 24rpx;
		color: #999;
	}

	.delete-btn {
		position: absolute;
		top: 0;
		right: 0;
		width: 40rpx;
		height: 40rpx;
		background-color: rgba(0, 0, 0, 0.5);
		color: white;
		font-size: 32rpx;
		line-height: 36rpx;
		text-align: center;
		border-bottom-left-radius: 12rpx;
		z-index: 1;
	}

	.delete-icon {
		line-height: 1;
	}

	.upload-status {
		position: absolute;
		bottom: 0;
		left: 0;
		right: 0;
		height: 40rpx;
		background-color: rgba(0, 0, 0, 0.5);
		color: white;
		font-size: 24rpx;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.uploading {
		/* 可以添加进度条背景 */
	}

	.error {
		background-color: #dd524d;
	}

	.success {
		background-color: #00c777;
	}
</style>